package cn.studyjava.verify.code12306.service.impl;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;

import org.patchca.background.BackgroundFactory;
import org.patchca.color.ColorFactory;
import org.patchca.filter.ConfigurableFilterFactory;
import org.patchca.filter.library.AbstractImageOp;
import org.patchca.filter.library.WobbleImageOp;
import org.patchca.font.FontFactory;
import org.patchca.service.Captcha;
import org.patchca.service.ConfigurableCaptchaService;
import org.patchca.text.renderer.BestFitTextRenderer;
import org.patchca.text.renderer.TextRenderer;
import org.patchca.word.WordFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import cn.studyjava.verify.code12306.common.Const;
import cn.studyjava.verify.code12306.common.enums.StyleEnum;
import cn.studyjava.verify.code12306.model.GroupParams;
import cn.studyjava.verify.code12306.model.GroupSingleton;
import cn.studyjava.verify.code12306.model.ImageParam;
import cn.studyjava.verify.code12306.model.ImgVerify;
import cn.studyjava.verify.code12306.model.Space;
import cn.studyjava.verify.code12306.service.IGenerateCodeService;
import cn.studyjava.verify.code12306.util.Base64Util;
import cn.studyjava.verify.code12306.util.GeneratorKey;
import cn.studyjava.verify.code12306.util.JsonUtil;
import cn.studyjava.verify.code12306.util.SpaceUtil;
import gui.ava.html.Html2Image;

/**
 * <p>
 * Description:
 * </p>
 * 
 * @author: zsljava
 * @version 1.0.0
 * <p>
 * History:
 * -----------------------------------------------
 * @Date: 2017年10月16日 下午3:29:42
 * @author: zsljava
 * @version 1.0.0
 * @OP: Create
 * -----------------------------------------------
 * </p>
 *
 * @since
 * @see
 */
@Service
public class GenerateCodeServiceImpl implements IGenerateCodeService {

	private static final Logger log = LoggerFactory.getLogger(GenerateCodeServiceImpl.class);

	@Autowired
	private StringRedisTemplate redisTemplate;
	
	/**
	 * 输出图片文件格式为png
	 */
	private final String OUT_IMG_TYPE = "png";

	ArrayList<Font> fontList = new ArrayList<Font>() {
		private static final long serialVersionUID = 1L;

		{
			Random random = new Random();
			add(new Font("微软雅黑", random.nextInt(10), 20));
			add(new Font("宋体", random.nextInt(10), 20));
			add(new Font("黑体", random.nextInt(10), 20));
			add(new Font("幼圆", random.nextInt(10), 20));
		}
	};

	@Override
	public void generate(Integer size) {
		for (int i = 0; i < size; i++) {
			generateCode();
		}
	}

	public void generateCode() {
		long startTime = System.currentTimeMillis();
		
		String verifyCode = GeneratorKey.key();
		
		// 随机取分组信息
		GroupParams gp = GroupSingleton.getInstance().get((int) (Math.random() * GroupSingleton.getInstance().size()));
		int randoms = (int) (Math.random() * gp.getImageParam().size());
		randoms = randoms > 2 ? 2 : randoms;
		List<ImageParam> showParams = new ArrayList<ImageParam>();
		List<ImageParam> params = new ArrayList<ImageParam>();
		for (int i = 0; i <= randoms; i++) {
			int rd = (int) (Math.random() * gp.getImageParam().size());
			params.add(gp.getImageParam().get(rd));
		}
		showParams = addOtherParams(gp.getName(), Const.STYLE.getFeatureValue(), params);
		// 随机插入打乱顺序
		Collections.shuffle(showParams);
		
		BufferedImage bufferedImage = verifyCode(gp.getName());
		ByteArrayOutputStream out = null;
		try {
			out = new ByteArrayOutputStream();
			if (ImageIO.write(bufferedImage, OUT_IMG_TYPE, out)) {
				String space = "";
				StringBuffer sb = new StringBuffer();
				// 水印
				sb.append("<body style='margin: 0; padding: 0'>");
				sb.append(
						"<table style='background-color:#E5EDF7;'><tr><td colspan=4 style='font-family: 微软雅黑;font-size: 18px;text-align: left; line-height: 25px; font-weight: none;'>请点击下图中<span style='color:red'>所有的</span>");
				sb.append("<img src='" + Base64Util.encodeImg(out.toByteArray()) + "' />");
				sb.append("</td></tr>");
				String name = "";
				if (Const.STYLE.getFeatureName().equals(StyleEnum.EIGHT.getFeatureName())) {
					for (int i = 0; i < showParams.size(); i++) {
						name = showParams.get(i).getName();
						if (name.equals(gp.getName())){
							space += i + ",";
						}
						if (i == 0 || i == 4){
							sb.append("<tr>");
						}
						sb.append("<td style='padding:" + Const.PADDING + "px;background-color: #ffffff;'><img width='" + Const.WIDTH + "' height='" + Const.HEIGHT
								+ "' src='" + showParams.get(i).getData() + "' /></td>");
						if (i == 3 || i == 7){
							sb.append("</tr>");
						}
					}
				} else if (Const.STYLE.getFeatureName().equals(StyleEnum.SIX.getFeatureName())) {
					for (int i = 0; i < showParams.size(); i++) {
						name = showParams.get(i).getName();
						if (name.equals(gp.getName())){
							space += i + ",";
						}
						if (i == 0 || i == 3){
							sb.append("<tr>");
						}
						sb.append("<td style='padding:" + Const.PADDING + "px;background-color: #ffffff;'><img width='" + Const.WIDTH + "' height='" + Const.HEIGHT
								+ "' src='" + showParams.get(i).getData() + "' /></td>");
						if (i == 2 || i == 5){
							sb.append("</tr>");
						}
					}
				} else if (Const.STYLE.getFeatureName().equals(StyleEnum.NINE.getFeatureName())) {
					for (int i = 0; i < showParams.size(); i++) {
						name = showParams.get(i).getName();
						if (name.equals(gp.getName())){
							space += i + ",";
						}
						if (i == 0 || i == 3 || i== 6){
							sb.append("<tr>");
						}
						sb.append("<td style='padding:" + Const.PADDING + "px;background-color: #ffffff;'><img width='" + Const.WIDTH + "' height='" + Const.HEIGHT
								+ "' src='" + showParams.get(i).getData() + "' /></td>");
						if (i == 2 || i == 5 || i == 8){
							sb.append("</tr>");
						}
					}
				}
				sb.append(
						"<tr><td colspan=2 style='font-family: 微软雅黑; text-align: left; font-size: 10px; color: #666666;font-weight: none;'>v2.1</td><td colspan=2 style='font-family: 微软雅黑; font-size: 10px; text-align: right; color: #666666;font-weight: none;'>studyjava.cn</td></tr>");
				sb.append("</table>");
				
				sb.append("</body>");
				log.info(gp.getName() + " 所在位置：" + space);
				Html2Image html2Image = Html2Image.fromHtml(sb.toString());
				html2Image.getImageRenderer().setWidth(100);
				BufferedImage buffImg = new BufferedImage(html2Image.getImageRenderer().getBufferedImage().getWidth(),
						html2Image.getImageRenderer().getBufferedImage().getHeight(), BufferedImage.TYPE_INT_RGB);
				buffImg.flush();
				Graphics2D g = buffImg.createGraphics();

				// 设置对线段的锯齿状边缘处理
				g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
				g.drawImage(html2Image.getImageRenderer().getBufferedImage().getScaledInstance(
						html2Image.getImageRenderer().getBufferedImage().getWidth(), html2Image.getImageRenderer().getBufferedImage().getHeight(),
						Image.SCALE_SMOOTH), 0, 0, null);
				// 透明度
				float alpha = 0.2f; 
				g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
				g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
				g.dispose();
				buffImg.flush();
				
				// 将二进制数据写进ByteArrayOutputStream
				out = new ByteArrayOutputStream();
				ImageIO.write(html2Image.getImageRenderer().getBufferedImage(), "png", out);
				// 输出数组
				ImgVerify verify = new ImgVerify();
				String[] spaceArr = space.split(",");
				List<Space> spaces = new ArrayList<>();
				for (String spa : spaceArr) {
					spaces.add(SpaceUtil.getSpace(Integer.valueOf(spa), Const.STYLE));
				}
				verify.setSpace(spaces);
				verify.setVerifyCode(verifyCode);
				verify.setVerifyType(Const.STYLE.getFeatureValue());
				verify.setWidth(html2Image.getImageRenderer().getBufferedImage().getWidth());
				verify.setVerifyImg(Base64Util.encodeImg(out.toByteArray()));
				verify.setCreateTime(new Date());
				verify.setVerifyName(gp.getName());
				redisTemplate.opsForSet().add(Const.VERIFY_CODE_SET, JsonUtil.obj2json(verify));
				long endTime = System.currentTimeMillis();
				log.info("生成验证码用时=>" + (endTime - startTime) + "ms");
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 生成文字图片验证码
	 * @param word
	 * @return
	 */
	private BufferedImage verifyCode(String word){
		ConfigurableCaptchaService configurableCaptchaService = new ConfigurableCaptchaService();
		// 颜色创建工厂,使用一定范围内的随机色
		// colorFactory = new RandomColorFactory();
		// configurableCaptchaService.setColorFactory(colorFactory);
		// 自定义背景
		MyCustomBackgroundFactory backgroundFactory = new MyCustomBackgroundFactory();
		configurableCaptchaService.setBackgroundFactory(backgroundFactory);
		// 图片滤镜设置
		ConfigurableFilterFactory filterFactory = new ConfigurableFilterFactory();
		List<BufferedImageOp> filters = new ArrayList<BufferedImageOp>();
		WobbleImageOp wobbleImageOp = new WobbleImageOp();
		wobbleImageOp.setEdgeMode(AbstractImageOp.EDGE_MIRROR);
		wobbleImageOp.setxAmplitude(2.0);
		wobbleImageOp.setyAmplitude(1.0);
		filters.add(wobbleImageOp);
		filterFactory.setFilters(filters);
		configurableCaptchaService.setFilterFactory(filterFactory);
		// 文字渲染器设置
		TextRenderer textRenderer = new BestFitTextRenderer();
		textRenderer.setBottomMargin(3);
		textRenderer.setTopMargin(3);
		configurableCaptchaService.setTextRenderer(textRenderer);
		// 验证码图片的大小
		configurableCaptchaService.setWidth(72);
		configurableCaptchaService.setHeight(20);
		
		WordFactory wf = new WordFactory() {
			@Override
			public String getNextWord() {
				return word;
			}
		};
		configurableCaptchaService.setWordFactory(wf);
		Random random = new Random();
		configurableCaptchaService.setColorFactory(new ColorFactory() {
			int i = random.nextInt(160);

			@Override
			public Color getColor(int x) {
				return new Color(i, i, i);
			}
		});
		
		FontFactory fontFactory = new FontFactory() {
			@Override
			public Font getFont(int arg0) {
				return fontList.get(random.nextInt(fontList.size()));
			}
		};
		configurableCaptchaService.setFontFactory(fontFactory);
		// 得到验证码对象,有验证码图片和验证码字符串
		Captcha captcha = configurableCaptchaService.getCaptcha();
		// 取得验证码图片并输出
		return captcha.getImage();
	}

	/**
	 * 添加图片
	 * @param name
	 * @param max
	 * @param params
	 * @return
	 */
	private List<ImageParam> addOtherParams(String name, Integer max, List<ImageParam> params) {
		int rd = (int) (Math.random() * GroupSingleton.getInstance().size());
		GroupParams gp = GroupSingleton.getInstance().get(rd);
		if (!gp.getName().equals(name)) {
			rd = (int) (Math.random() * gp.getImageParam().size());
			ImageParam ip = gp.getImageParam().get(rd);
			params.add(ip);
			if (params.size() < max) {
				addOtherParams(name, max, params);
			}
		} else {
			addOtherParams(name, max, params);
		}
		return params;
	}

	/**
	 * 自定义验证码图片背景,主要画一些噪点和干扰线
	 */
	class MyCustomBackgroundFactory implements BackgroundFactory {

		private Random random = new Random();
		
		/**
		 * 画100个噪点(颜色及位置随机)
		 */
		private final int NUM = 100;
		
		@Override
		public void fillBackground(BufferedImage image) {
			Graphics graphics = image.getGraphics();
			// 验证码图片的宽高
			int imgWidth = image.getWidth();
			int imgHeight = image.getHeight();
			// 填充为白色背景
			graphics.setColor(new Color(229, 237, 247));
			graphics.fillRect(0, 0, imgWidth, imgHeight);
			// 画NUM个噪点(颜色及位置随机)
			for (int i = 0; i < NUM; i++) {
				// 随机颜色
				int rInt = random.nextInt(255);
				int gInt = random.nextInt(255);
				int bInt = random.nextInt(255);
				graphics.setColor(new Color(rInt, gInt, bInt));
				// 随机位置
				int xInt = random.nextInt(imgWidth - 3);
				int yInt = random.nextInt(imgHeight - 2);
				// 随机旋转角度
				int sAngleInt = random.nextInt(360);
				int eAngleInt = random.nextInt(360);
				// 随机大小
				int wInt = random.nextInt(6);
				int hInt = random.nextInt(6);
				graphics.fillArc(xInt, yInt, wInt, hInt, sAngleInt, eAngleInt);
				// 画5条干扰线
				if (i % 20 == 0) {
					int xInt2 = random.nextInt(imgWidth);
					int yInt2 = random.nextInt(imgHeight);
					graphics.drawLine(xInt, yInt, xInt2, yInt2);
				}
			}
		}
	}
}
